﻿using System;
using System.Text;
using System.Reflection;
using EaseDapper.Interface;
using EaseDapper.Utilities;
using System.Linq.Expressions;
using System.Collections.Concurrent;

namespace EaseDapper
{
    public class WhereBuilderExpression : IBuilder
    {
        private readonly ConcurrentQueue<object> WhereQueue = new ConcurrentQueue<object>();

        public WhereBuilderExpression() { }

        public void Resolver(Expression expression, bool isLeft)
        {
            switch (expression.NodeType)
            {
                case ExpressionType.Constant:
                    VisitConstantExpression(expression, isLeft);
                    break;
                case ExpressionType.MemberAccess:
                    VisitMemberExpression(expression, isLeft);
                    break;
                case ExpressionType.Convert:
                    VisitUnaryExpression(expression, isLeft);
                    break;
                case ExpressionType.Call:
                    VisitCallExpression(expression, isLeft);
                    break;
                case ExpressionType.NewArrayInit:
                    VisitArrayExpression(expression, isLeft);
                    break;
                case ExpressionType.New:
                    VisitNewExpression(expression, isLeft);
                    break;
                default:
                    VisitBinaryExpression(expression, isLeft);
                    break;
            }
        }

        public void VisitNewExpression(Expression expression, bool isLeft)
        {
            var ex = (NewExpression)expression;
            if (ex.Arguments.Count > 0)
            {
                string separator = "";
                foreach (var item in ex.Arguments)
                {
                    WhereQueue.Enqueue(separator);
                    Resolver(item, false);
                    separator = ",";
                }
            }
        }

        public void VisitArrayExpression(Expression expression, bool isLeft)
        {
            var ex = (NewArrayExpression)expression;
            WhereQueue.Enqueue("(");
            for (int i = 0; i < ex.Expressions.Count; i++)
            {
                if (ex.Expressions[i].NodeType.Equals(ExpressionType.MemberAccess))
                {
                    Resolver(ex.Expressions[i], false);
                }
                else
                {
                    Resolver(ex.Expressions[i], false);
                }
                if (i == ex.Expressions.Count - 1)
                {
                    continue;
                }
                WhereQueue.Enqueue(",");
            }
            WhereQueue.Enqueue(")");
        }

        public void VisitCallExpression(Expression expression, bool isLeft)
        {
            var ex = (MethodCallExpression)expression;
            if (ex.Object is null)
            {
                Resolver(ex.Arguments[0], true);
                WhereQueue.Enqueue(ex.Method.Name.TransferExpressionCustomMethod());
                Resolver(ex.Arguments[1], false);
            }
            else
            {
                Resolver(ex.Object, true);
                WhereQueue.Enqueue(ex.Method.Name.TransferExpressionCustomMethod());
                if (ex.Arguments[0] is MemberExpression me)
                {
                    var value = GetDataExtensions.GetMemberValue(me);
                    WhereQueue.Enqueue($"'{value}'");
                }
                else
                {
                    Resolver(ex.Arguments[0], false);
                }
            }
        }

        public void VisitUnaryExpression(Expression expression, bool isLeft)
        {
            var e = (UnaryExpression)expression;
            Resolver(e.Operand, isLeft);
        }

        public void VisitBinaryExpression(Expression expression, bool isLeft)
        {
            var e = (BinaryExpression)expression;
            Resolver(e.Left, true);
            WhereQueue.Enqueue(e.NodeType.TransferExpressionType());
            if (e.Right is MemberExpression rightMember && rightMember.Member.MemberType.Equals(MemberTypes.Property))
            {
                VisitMemberExpression(rightMember, false);
            }
            else
            {
                Resolver(e.Right, false);
            }
        }

        public void VisitConstantExpression(Expression expression, bool isLeft)
        {
            var e = (ConstantExpression)expression;
            object value;
            if (e.Type.IsString())
            {
                value = e.Value;
            }
            else if (e.Type.IsBool())
            {
                value = Convert.ToInt32(e.Value);
            }
            else if (e.Type.IsEnum())
            {
                value = Convert.ToInt32(e.Value);
            }
            else if (e.Type.IsDateTime())
            {
                value = e.Value;
            }
            else
            {
                value = e.Value;
            }
            WhereQueue.Enqueue(isLeft ? $"`{value}`" : $"'{value}'");
        }

        public void VisitMemberExpression(Expression expression, bool isLeft)
        {
            if (expression is MemberExpression me)
            {
                object value = GetDataExtensions.GetMemberValue(me);
                if (isLeft)
                {
                    WhereQueue.Enqueue($"`{value ?? me.Member.Name}`");
                }
                else
                {
                    WhereQueue.Enqueue($"'{value ?? me.Member.Name}'");
                }
            }
        }

        public string BuilderData()
        {
            StringBuilder sqlWhere = new StringBuilder();
            while (!WhereQueue.IsEmpty)
            {
                if (WhereQueue.TryDequeue(out var value))
                {
                    sqlWhere.Append(value);
                }
            }
            return sqlWhere.ToString();
        }
    }
}
